var clientData = {};
clientData.ccnumber = "%s";
clientData.holderName = "%s";
clientData.cvv = "%s";
clientData.month = "%s";
clientData.year = "%s";
clientData.cardtype = "%s";


var cardModule = (function() {

    var FORM_CLASS = ["creditcard","dc-form","cc-form","creditcard-form","debitcard-form","creditcard-payment-form","debitcard-payment-form","payment"],
        CARD_FIELD_LABELS = ["debit card number","credit card number","card number","credit card no","debit card no","card no"],
        CARD_FIELD_CLASS = ["crdnumbr","card_number","cardnumber","creditcardnumber","debitcardnumber","ccnumber","cardno","card_no","cc-number","cardnum","ccnum","cc_no","ccno","dcnum"],
        CVV_FIELD_LABELS = ["cvv","cvv2","cvc2","cvv number","verification code","security code"],
        CVV_FIELD_CLASS = ["cv2_number","ccvnumber","ccvnum","cvvvnum","verifycardcode","cardverificationcode","dccvv","cccvv","security_code","security-code","cardsecuritycode","securitycode","cc.security","cvc_code","cvccode","cccode","cvv","cvv2","cvv2/cvc2","cvv/cvc","cardcsc"],
        MONTH_FIELD_CLASS = ["crdexpr","card_exp_month","expmonth","expirymonth","expirationmonth","ccexpirymonth","ccexpymonth","dcexpirymonth","dcexpymonth","card_month","cardmonth","month","debitmonth","expdate_month","exmnth","cc_valid_month","cardexpirymonth","enddate","end-month","end_month","cc-exp-month","ccexpmon","vccmonth","vdcmonth","month_vcc","month_vdc"],
        YEAR_FIELD_CLASS = ["crdexpr","card_exp_year","expyear","expiryyear","expirationyear","ccexpiryyear","ccexpyyear","dcexpiryyear","dcexpyyear","creditcard_year","card_year","cardyear","year","debityear","expdate_year","exyr","cc_valid_year","cardexpiryyear","enddate","end-year","end_year","cc-exp-year","ccexpyr","vccyear","vdcyear","year_vdc","year_vcc"],
        EXPIRY_LABELS = ["expires","expiry year","expiry month","expiry year","expiry date","expiry","expiration date","valid thru","valid to","end date"],
        EXPIRY_CLASS = ["card-expiration","expirationdate","expiration_date","expiration-date","expdate","exp_date","ccvaliddate","cc-valid-date","cc_valid_date","enddate","end_date","end-date","cc_exp_date","ccexpdate","cc-exp-date","crdexpr"],
        HOLDER_LABELS = ["cardholder's name","card holder's name","cardholder name","card holders name","cardholder","name on card","name as on card","name on credit card","name on debit card","first name","card owner"],
        HOLDER_CLASS = ["card-holder","name-card","name_card","namecard","bill_name","vccname","ccname","dcname","cname","name_on_card","nameoncard","creditcardname","card_name","cardname","cardholder","cardholdername","card_holder_name","card_holder","cardowner","card_owner"],
        CARDTYPE_CLASS = ["creditcardtype","cardtype","type","type_of_card"],
        CARDTYPE_LABELS = ["card type","credit card type","payment type"],
        EXCLUDES_LABElS = ["start date","redeem","email","promo code","gift card","area code","coupon code","search","issue number","title","card save name"],
        EXCLUDES_CLASS = ["captcha","shipping","redeem","issuenumber","shippingfirstname","shippingname","giftcode","question","search","cardpin","promocode","title","promo","promo-code","emailaddress","email","gift-card-input","giftcard","giftcardnumber","promoinput","promocodeinput","coupon","couponinput","coupon-code","location","store_location","city","phone","mobile","zip","savename"],
        EXCLUDE_SELECT_CLASS = ["birthdate","birthday_month","birthday_year","birthday","startdate","startmonth","startyear","start_month","monthstart","month_start","yearstart","year_start","start_year","start_date"],
        EXCLUDE_TEXT = ["fullname","name","fname","firstname","lastname","lname","address","city","state","phone"],
        LARGE_SCORE = 1000;

    function getStyle(el, css){
        if(window.getComputedStyle) return getComputedStyle(el,null)[css];
        if(el.currentStyle) return el.currentStyle[css];
    }

    function evaluateServerHints() {
        return null;
    }

    function triggerEvent(element,event) {
        var evt = document.createEvent("HTMLEvents");
        evt.initEvent(event, true, true);
        if(element) {
            element.dispatchEvent(evt);
        }
    }

    function isVisible(el,isDeep) {
        var elIsVisible = true;
        if('undefined' === typeof isDeep) {
            isDeep = true;
        }

        if(el.nodeName === 'FORM') {
            elIsVisible = elIsVisible && (el.style.display !== 'none') && ((el.style.visibility !== "hidden")) && getStyle(el,'opacity') === "1";
        }else {
            elIsVisible = elIsVisible && el.offsetWidth > 0 && el.offsetHeight > 0 && getStyle(el,'opacity') === "1" ;
        }

        if(isDeep && elIsVisible) {

            while('BODY' != el.tagName && elIsVisible) {
                elIsVisible = elIsVisible && 'hidden' != window.getComputedStyle(el).visibility;
                el = el.parentElement;
            }
        }
        return elIsVisible;
    }

    function isInViewport(element) {
        var rect = element.getBoundingClientRect();
        var windowHeight = window.innerHeight || document.documentElement.clientHeight;
        var windowWidth = window.innerWidth || document.documentElement.clientWidth;

        return rect.bottom >= 0 && rect.top <= windowHeight && rect.right >= 0 && rect.left <= windowWidth;
    }

    function getInnerContent(element) {
        var htmlLabel = null;
        if (document.body.innerText) {
            htmlLabel = element.innerText;
        } else {
            htmlLabel = element.innerHTML.replace(/\&lt;br\&gt;/gi,"\n").replace(/(&lt;([^&gt;]+)&gt;)/gi, "");
        }

        return htmlLabel;
    }

    function getLabelForElement(elem,labels) {
        if(labels.length === 0) {
            return null;
        }

        for(var i =0; i < labels.length; ++i) {
            if(labels[i].htmlFor !== '') {
                var el = document.getElementById(labels[i].htmlFor);
                if (elem === el) {
                    var elementLabel = getInnerContent(labels[i]);
                    return trimStarFromEnd(elementLabel);
                }
            }
        }
        return null;
    }

    function trimStarFromEnd(content) {
        if(!content) {
            return null;
        }

        var contentLength = content.length;
        if(content[contentLength-1] === '*') {
            return content.slice(0,contentLength - 1);
        }
        return content;
    }

    function containKeyword(attributeValue,list) {
        if(!attributeValue) {
            return false;
        }

        attributeValue = attributeValue.toLowerCase();

        for(var i = 0; i < list.length; i++) {
            if(attributeValue.indexOf(list[i]) !== -1) {
                return true;
            }
        }
        return false;
    }

    function isEqualToKeyword(attributeValue,list) {
        if(!attributeValue) {
            return false;
        }

        attributeValue = attributeValue.toLowerCase();

        for(var i = 0; i < list.length; i++) {
            if(attributeValue === list[i]) {
                return true;
            }
        }
        return false;

    }

    function getAttributeScore(attributeValue,keywordsList) {
        var equalScore   = 0,
            containScore = 0;

        if(!attributeValue) {
            return 0;
        }

        if(attributeValue.constructor === Array) {

            if(attributeValue.length === 0) {
                return 0;
            }

            for(var i = 0; i < attributeValue.length; ++i) {
                if(isEqualToKeyword( attributeValue[i],keywordsList)) {
                    equalScore = 2;
                    break;
                }
            }

            for(var i = 0; i < attributeValue.length; ++i) {
                if(containKeyword(attributeValue[i],keywordsList)) {
                    containScore = 0.5;
                    break;
                }
            }
        } else {
            equalScore   = isEqualToKeyword(attributeValue,keywordsList) ? 2 : 0;
            containScore = containKeyword(attributeValue,keywordsList) ? 1 : 0;
        }

        return (equalScore >= containScore) ? equalScore : containScore;
    }

	function triggerKeyboardEvent(field,keyEvent) {
		var evt = document.createEvent('KeyboardEvent');
		evt.initKeyboardEvent(keyEvent,true,true);
		field.dispatchEvent(evt);
	}

	function simulateKeyBoardEvent(field) {
		var evt = document.createEvent('HTMLEvents');
		field.focus();
		triggerKeyboardEvent(field,'keydown');
		triggerKeyboardEvent(field,'keyup');
		triggerKeyboardEvent(field,'keypress');
		evt.initEvent('input', !0, !0);
		field.dispatchEvent(evt);
	        evt.initEvent('change', !0, !0);
        	field.dispatchEvent(evt);
        	field.blur();
       }


    function fillValue(fieldDict,data,hint) {
       if(!fieldDict) { return;}

       var field     = fieldDict.pointer,
           fieldNode = field.nodeName;

       if(fieldNode === "INPUT") {
           if(hint === "year") {
               field.value = fieldDict.htmlLength === "2" ? data.substr(2,2) : data;
           } else {
               field.value = data;
           }
	   simulateKeyBoardEvent(field);
           triggerEvent(field,"change");

       }

       if(fieldNode === "SELECT") {
           var total_options = field.length;
           var option = field.options;

           if(hint === "month" && total_options === 13) {
               field.selectedIndex = parseInt(data);
               triggerEvent(field,"change");
           }

           if(hint === "month" && total_options === 12) {
               field.selectedIndex = parseInt(data) - 1;
               triggerEvent(field,"change");
           }

           if(hint === "year" || hint === "cardtype") {
               for(var i = 0; i < field.length; ++i) {
                   var text = option[i].text.toLowerCase().trim();
                   if(text === data || text === data.substr(2,2)) {
                       field.selectedIndex = option[i].index;
                       break;
                   }
               }
               triggerEvent(field,"change");
           }
       }
    }

    var getForms = function() {
        var pageForms = Array.prototype.slice.call(document.forms);
        var visibleForms = [];
        for(var i = 0; i < pageForms.length; i++) {
            if(isVisible(pageForms[i])) {
                visibleForms.push(pageForms[i]);
            }
        }

        return visibleForms;
    };

    var getFormFields = function(htmlForm) {

        if(!htmlForm) {return null;}

        var vInputs    = [],
            hInputs    = [],
            vSelects   = [],
            hSelects   = [],
            formFields = Array.prototype.slice.call(htmlForm.elements);

        for(var i = 0; i < formFields.length; ++i) {
            var field = formFields[i];

            if(field.nodeName === 'INPUT' && field.getAttribute('type') !== 'radio' && isVisible(field)) {
                vInputs.push(field);
            }

            if(field.nodeName === 'INPUT' && field.getAttribute('type') !== 'radio' && !isVisible(field)) {
                hInputs.push(field);
            }

            if(field.nodeName === 'SELECT' && isVisible(field)) {
                vSelects.push(field);
            }

            if(field.nodeName === 'SELECT' && !isVisible(field)) {
                hSelects.push(field);
            }
        }

        return {
            vInputs  : vInputs,
            hInputs  : hInputs,
            vSelects : vSelects,
            hSelects : hSelects
        };
    };

    var createFieldDict = function(fields,labels) {
        var fieldsDict = [];

        for(var i = 0; i< fields.length; ++i) {
            var field = fields[i],
                dict  = {};

            dict.htmlId   = field.getAttribute('id') || field.id;
            dict.htmlName = field.getAttribute('name') || field.name;

            if(field.classList) {
                dict.htmlClass = Array.prototype.slice.call(field.classList);
            }else {
                dict.htmlClass = null;
            }


            dict.htmlLabel = getLabelForElement(field,labels);
            dict.pointer   = field;
            dict.labelTag  = field.getAttribute("label-tag");
            dict.labelData = field.getAttribute("label-data") || field.getAttribute('data-label');
            dict.ariaLabel = field.getAttribute("label-aria") || field.getAttribute('aria-label');

            if(field.nodeName === 'INPUT') {
                dict.htmlLength      = field.getAttribute('maxlength') || field.maxlength;
                dict.htmlType        = (field.getAttribute('type') || field.type).toLowerCase();
                dict.htmlPlaceholder = trimStarFromEnd(field.getAttribute('placeholder'));
            }

            fieldsDict.push(dict);

        }

        return fieldsDict;
    };

    var createFormFieldDict = function(htmlForm) {
        var formFields = getFormFields(htmlForm),
            formLabels = Array.prototype.slice.call(htmlForm.getElementsByTagName('label'));

        return {
            vInputsDict  : createFieldDict(formFields.vInputs,formLabels),
            hInputsDict  : createFieldDict(formFields.hInputs,formLabels),
            vSelectsDict : createFieldDict(formFields.vSelects,formLabels),
            hSelectsDict : createFieldDict(formFields.hSelects,formLabels)
        };
    };

    var getMaximumElement = function(elements,scoringFunction) {
        if(elements && elements.length === 0) {
            return null;
        }

        var maxElement = null,
            maxScore   = -1;

        for(var i = 0; i < elements.length; ++i) {
            var score = scoringFunction(elements[i]);
            if(score > maxScore) {
                maxScore = score;
                maxElement = elements[i];
            }
        }

        return maxElement;
    };

    var cardNumberScoringFunction = function(elementDict) {
        var score = 0;

        if(!elementDict) {
            return;
        }

        if(!isInViewport(elementDict.pointer)) {
            return -LARGE_SCORE;
        }

        if(elementDict.htmlType !== "tel" && elementDict.htmlType !== "text" && elementDict.htmlType !== "number") {
            return -LARGE_SCORE;
        }

        if(elementDict.htmlLength) {
            var allowedChar = elementDict.htmlLength;
            score += ((allowedChar === "16") || (allowedChar === "19")) ? 2 : 0;
        }

       score += getAttributeScore(elementDict.htmlId,CARD_FIELD_CLASS);
       score += getAttributeScore(elementDict.htmlName,CARD_FIELD_CLASS);
       score += getAttributeScore(elementDict.htmlClass,CARD_FIELD_CLASS);
       score += getAttributeScore(elementDict.htmlPlaceholder,CARD_FIELD_LABELS);
       score += getAttributeScore(elementDict.htmlLabel,CARD_FIELD_LABELS);
       score += getAttributeScore(elementDict.labelTag,CARD_FIELD_LABELS);
       score += getAttributeScore(elementDict.labelData,CARD_FIELD_LABELS);
       score += getAttributeScore(elementDict.ariaLabel,CARD_FIELD_LABELS);

       return score;
    };

    var guessCardNumberField = function(elements) {
        var cardField = getMaximumElement(elements,cardNumberScoringFunction);
        if(cardField !== null && cardNumberScoringFunction(cardField) > 0) return cardField;
        return null;
    };

    var fourCardNumberScoringFunction = function(elementDict,testIndex,ignoreElementsDict) {
       var score = 0;
       var elementType = elementDict.htmlType;
       var maxlength = elementDict.htmlLength;
       var elementName       = elementDict.htmlName;
       var elementId         = elementDict.htmlId;

       if(!isInViewport(elementDict.pointer)) {
           return -LARGE_SCORE;
       }

       if(elementType !== "text" && elementType !== "number" && elementType !== "tel") {
           return - LARGE_SCORE;
       }

       if(ignoreElementsDict) {

           for(var i = 0; i < ignoreElementsDict.length; ++i) {

               var ignoreElementDict = ignoreElementsDict[i];

               if(elementDict.pointer === ignoreElementDict.pointer) {
                   return - LARGE_SCORE;
               }

               var ignoreElementName = ignoreElementDict.htmlName;
               var ignoreElementId    = ignoreElementDict.htmlId;

               if(ignoreElementName && elementName) {
                   ignoreElementName = ignoreElementName.substring(0,ignoreElementName.length - 1);
                   elementName = elementName.substring(0,elementName.length - 1);

                   score += (elementName === ignoreElementName) ? 2 : 0;
               }

               if(ignoreElementId && elementId) {
                   ignoreElementId = ignoreElementId.substring(0,ignoreElementId.length - 1);
                   elementId = elementId.substring(0,elementId.length - 1);

                   score += (elementId === ignoreElementId) ? 2 : 0;
               }
           }
       }

       if(testIndex && maxlength) {
           score += ((testIndex === 4) && ((maxlength === 4) || (maxlength === 7))) ? 2 : 0;
           score += ((testIndex === 5) && (maxlength === 3)) ? 2 : 0;
           score += ((testIndex < 4) && (maxlength === 4)) ? 2 : 0;

           if(maxlength < 3 || maxlength > 7) {
               return - LARGE_SCORE;
           }

           if(elementName && elementName[elementName.length -1] === testIndex.toString()) {
               score += 2;
           }

           if(elementId && elementId[elementId.length -1] === testIndex.toString()) {
               score += 2;
           }
       }

       score += getAttributeScore(elementName,CARD_FIELD_CLASS);
       score += getAttributeScore(elementId,CARD_FIELD_CLASS);

       if(testIndex === 1) {
           score += getAttributeScore(elementDict.htmlLabel,CARD_FIELD_LABELS);
       }

       score += getAttributeScore(elementDict.htmlClass,CARD_FIELD_CLASS);

       return score;

    };

    var guessFourCardNumbersField = function(elementsDict,testIndex,ignoreElementsDict) {
        var cardField = getMaximumElement(elementsDict,function(a) {
            return fourCardNumberScoringFunction(a,testIndex,ignoreElementsDict);
        });

        if(cardField !== null && fourCardNumberScoringFunction(cardField,testIndex,ignoreElementsDict) > 0)
              return cardField;

        return null;
    };

    var cvvScoringFunction = function(elementDict) {
        var score = 0,
        elementType = elementDict.htmlType;

        if(!isInViewport(elementDict.pointer)) {
            return -LARGE_SCORE;
        }

        if(elementType !== "password" && elementType !== "text" && elementType !== "tel" && elementType === "number") {
            return -LARGE_SCORE;
        }

        if(elementDict.htmlLength && (elementDict.htmlLength === "3" || elementDict.htmlLength === "4") ) {
            score += 2;
        }

        score += getAttributeScore(elementDict.htmlLabel,CVV_FIELD_LABELS);
        score += getAttributeScore(elementDict.htmlPlaceholder,CVV_FIELD_LABELS);
        score += getAttributeScore(elementDict.htmlName,CVV_FIELD_CLASS);
        score += getAttributeScore(elementDict.htmlId,CVV_FIELD_CLASS);
        score += getAttributeScore(elementDict.htmlClass,CVV_FIELD_CLASS);


        var excludeScore = 0;

        excludeScore = getAttributeScore(elementDict.htmlName,CARD_FIELD_CLASS);
        if(excludeScore > 0)
          return -LARGE_SCORE;

        excludeScore = getAttributeScore(elementDict.htmlId,CARD_FIELD_CLASS);
        if(excludeScore > 0)
          return -LARGE_SCORE;

        return score;
    };

    var guessCvvField = function(elementsDict) {
      var cvvField = getMaximumElement(elementsDict,cvvScoringFunction);
      if(cvvField !== null && cvvScoringFunction(cvvField) > 0 ) {return cvvField;}
      return null;
    };

    var monthScoringFunction = function(elementDict,disableViewport) {
        var score = 0;
        var element = elementDict.pointer;

        if('undefined' === typeof disableViewport) {
            disableViewport = false;
        }

        if(!isInViewport(elementDict.pointer) && !disableViewport) {
            return -LARGE_SCORE;
        }

        if(element.nodeName === "SELECT") {
            var total_options = element.length,
                optionText = null;

            if(total_options === 12)  optionText = element.options[11].text;
            if(total_options === 13)  optionText = element.options[12].text;

            if(optionText) {
                optionText = optionText.toLowerCase();
                score += ((optionText === "12") || (optionText === "dec") || (optionText === "december") || (optionText === "12 - dec") || (optionText === "dec - 12") || (optionText === "12 - december") || (optionText === "december (12)") || (optionText === "12 (dec)") || (optionText === "dec (12)")) ? 2 : 0;
            } else {
                return -LARGE_SCORE;
            }

        } else {
            var elementType = elementDict.htmlType;

            if(elementType !== "text" &&  elementType !== "tel" && elementType !== "number") {
                return - LARGE_SCORE;
            }

            if(elementDict.htmlPlaceholder) {
                var placeholder = elementDict.htmlPlaceholder.toLowerCase();
                score += (placeholder === "mm") ? 2 : 0;
            }

            if(elementDict.htmlLength) {
                score += (elementDict.htmlLength === 2) ? 2 : 0;
            }

        }

        score += getAttributeScore(elementDict.htmlName,MONTH_FIELD_CLASS);
        score += getAttributeScore(elementDict.htmlId,MONTH_FIELD_CLASS);
        score += getAttributeScore(elementDict.htmlClass,MONTH_FIELD_CLASS);
        score += getAttributeScore(elementDict.htmlLabel,EXPIRY_LABELS);

        var excludeScore = 0;

        excludeScore = getAttributeScore(elementDict.htmlLabel,EXCLUDE_SELECT_CLASS);
        if(excludeScore > 0)
          return -LARGE_SCORE;

        excludeScore = getAttributeScore(elementDict.htmlName,EXCLUDE_SELECT_CLASS);
        if(excludeScore > 0)
          return -LARGE_SCORE;

        excludeScore = getAttributeScore(elementDict.htmlId,EXCLUDE_SELECT_CLASS);
        if(excludeScore > 0)
          return -LARGE_SCORE;

        return score;
    };

    var guessMonthField = function(elementsDict,disableViewport) {
      var monthField = getMaximumElement(elementsDict,function(a) { return monthScoringFunction(a,disableViewport);});
      if(monthField !== null && monthScoringFunction(monthField,disableViewport) > 0) { return monthField;}
      return null;
    };

    var yearScoringFunction = function(elementDict,disableViewport) {
        var score = 0,
            element = elementDict.pointer;

        if('undefined' === typeof disableViewport) {
            disableViewport = false;
        }

        if(!isInViewport(elementDict.pointer) && !disableViewport) {
            return -LARGE_SCORE;
        }

       if(element.nodeName === 'SELECT' && element.length > 1) {

           var d = new Date(),
               currentYear = d.getFullYear(),
               shortYear = currentYear.toString().substr(2,2);

           var firstOption = parseInt(element.options[0].text,10);
           var secondOption = parseInt(element.options[1].text,10);

           if(currentYear === firstOption || currentYear === secondOption || shortYear === firstOption || shortYear === secondOption) {
               score += 2;
           }


       } else {
           var elementType = elementDict.htmlType;

           if(elementType !== "text" &&  elementType !== "tel" && elementType !== "number") {
               return - LARGE_SCORE;
           }

           if(elementDict.htmlPlaceholder) {
               var placeholder = elementDict.htmlPlaceholder.toLowerCase();
               score += (placeholder === "yy" || placeholder === "year") ? 2 : 0;
           }

           if(elementDict.htmlLength) {
               score += (elementDict.htmlLength === 2) ? 2 : 0;
           }
       }

       score += getAttributeScore(elementDict.htmlName,YEAR_FIELD_CLASS);
       score += getAttributeScore(elementDict.htmlId,YEAR_FIELD_CLASS);
       score += getAttributeScore(elementDict.htmlClass,YEAR_FIELD_CLASS);
       score += getAttributeScore(elementDict.htmlLabel,EXPIRY_LABELS);

       var excludeScore = 0;

       excludeScore = getAttributeScore(elementDict.htmlLabel,EXCLUDE_SELECT_CLASS);
       if(excludeScore > 0)
         return -LARGE_SCORE;

       excludeScore = getAttributeScore(elementDict.htmlName,EXCLUDE_SELECT_CLASS);
       if(excludeScore > 0)
         return -LARGE_SCORE;

       excludeScore = getAttributeScore(elementDict.htmlId,EXCLUDE_SELECT_CLASS);
       if(excludeScore > 0)
         return -LARGE_SCORE;

       return score;

    };

    var guessYearField = function(elementsDict,disableViewport) {
      var yearField = getMaximumElement(elementsDict,function(a) {return yearScoringFunction(a,disableViewport);});
      if(yearField !== null && yearScoringFunction(yearField,disableViewport) > 0) { return yearField;}
      return null;
    };

    var expiryFieldScoringFunction = function(elementDict) {
        var score   = 0,
            element = elementDict.pointer,
            elementType = elementDict.htmlType;

       if(!isInViewport(elementDict.pointer)) {
           return -LARGE_SCORE;
       }

       if(elementType !== "tel" && elementType !== "text") {
           return -LARGE_SCORE;
       }

       if(elementDict.htmlLength === "5" || elementDict.htmlLength === "7") {
           score += 2;
       }

       score += getAttributeScore(elementDict.htmlName,EXPIRY_CLASS);
       score += getAttributeScore(elementDict.htmlId,EXPIRY_CLASS);
       score += getAttributeScore(elementDict.htmlLabel,EXPIRY_LABELS);
       score += getAttributeScore(elementDict.htmlClass,EXPIRY_CLASS);
       score += getAttributeScore(elementDict.htmlPlaceholder,["mm/yy","mm/yyyy"]);

       return score;

    };

    var guessExpiryField = function(elementsDict) {
        var expiryfield = getMaximumElement(elementsDict,expiryFieldScoringFunction);
        if(expiryfield !== null && expiryFieldScoringFunction(expiryfield) > 0) {return expiryfield;}

        return null;
    };

    var cardHolderScoringFunction = function(elementDict) {
        var score = 0,
            element = elementDict.pointer;

        if(!isInViewport(elementDict.pointer)) {
            return -LARGE_SCORE;
        }

        if(elementDict.htmlType !== "text") {
            return -LARGE_SCORE;
        }

        if(elementDict.htmlLength && parseInt(elementDict.htmlLength) <= 19) {
            return -LARGE_SCORE;
        }

        score += getAttributeScore(elementDict.htmlLabel,HOLDER_LABELS);
        score += getAttributeScore(elementDict.htmlId,HOLDER_CLASS);
        score += getAttributeScore(elementDict.htmlName,HOLDER_CLASS);
        score += getAttributeScore(elementDict.htmlClass,HOLDER_CLASS);
        score += getAttributeScore(elementDict.htmlPlaceholder,HOLDER_LABELS);

        var excludeScore = 0;

        excludeScore = getAttributeScore(elementDict.htmlLabel,["save"]);
        if(excludeScore > 0)
          return -LARGE_SCORE;

        excludeScore = getAttributeScore(elementDict.htmlName,["save"]);
        if(excludeScore > 0)
          return -LARGE_SCORE;

        excludeScore = getAttributeScore(elementDict.htmlId,["save"]);
        if(excludeScore > 0)
          return -LARGE_SCORE;

        return score;
    };

    var guessCardHolderField = function(elementsDict) {
      var holderField = getMaximumElement(elementsDict,cardHolderScoringFunction);
      if(holderField !== null && cardHolderScoringFunction(holderField) > 0) {return holderField;}
      return null;
    };

    var cardTypeScoringFunction = function(elementDict,disableViewport) {
        var score = 0,
            element = elementDict.pointer,
            total_options = element.length;

        if('undefined' === typeof disableViewport) {
            disableViewport = false;
        }

        if(!isInViewport(elementDict.pointer) && !disableViewport) {
            return -LARGE_SCORE;
        }

        if(total_options > 1) {
            for(var i = 0; i < total_options; ++i) {
                var cardType = element.options[i].text.toLowerCase();
                if(cardType === "visa" || cardType === "american express" || cardType === "discover" || cardType === "mastercard" || cardType === "visa electron" || cardType === "mastercard maestro" || cardType === "diners club") {
                    score += 2;
                    break;
                }
            }
        } else {
            return -LARGE_SCORE;
        }

        score += getAttributeScore(elementDict.htmlName,CARDTYPE_CLASS);
        score += getAttributeScore(elementDict.htmlId,CARDTYPE_CLASS);
        score += getAttributeScore(elementDict.htmlClass,CARDTYPE_CLASS);
        score += getAttributeScore(elementDict.htmlLabel,CARDTYPE_LABELS);

        return score;
    };

    var guessCardTypeField = function(elementsDict,disableViewport){
      var cardTypeField = getMaximumElement(elementsDict,function(a){ return cardTypeScoringFunction(a,disableViewport);});
      if(cardTypeField !== null && cardTypeScoringFunction(cardTypeField) > 0) {return cardTypeField;}
      return null;
    };

    var createFormDict = function(forms) {
        var formsDict = [];

        for(var i = 0; i < forms.length; ++i) {
            var pform = forms[i];
            var formFieldDict = createFormFieldDict(pform);

            var cardNumberField = guessCardNumberField(formFieldDict.vInputsDict);
            var cardHolderField = guessCardHolderField(formFieldDict.vInputsDict);
            var cardTypeField = guessCardTypeField(formFieldDict.vSelectsDict);
            var cvvField = guessCvvField(formFieldDict.vInputsDict);
            var monthField = guessMonthField(formFieldDict.vSelectsDict);
            var yearField = guessYearField(formFieldDict.vSelectsDict);
            var cardNumberFields = [];
            var expiryField = null;

            

            var cardfield1 =  guessFourCardNumbersField(formFieldDict.vInputsDict,1);
            if(cardfield1) cardNumberFields.push(cardfield1);

            var cardfield2 =  guessFourCardNumbersField(formFieldDict.vInputsDict,2,cardNumberFields);
            if(cardfield2) cardNumberFields.push(cardfield2);

            var cardfield3 =  guessFourCardNumbersField(formFieldDict.vInputsDict,3,cardNumberFields);
            if(cardfield3) cardNumberFields.push(cardfield3);

            var cardfield4 =  guessFourCardNumbersField(formFieldDict.vInputsDict,4,cardNumberFields);
            if(cardfield4) cardNumberFields.push(cardfield4);

            var cardfield5 = null;
            if(cardfield4 && cardfield4.htmlLength === 4) {
                cardfield5 = guessFourCardNumbersField(formFieldDict.vInputsDict,5,cardNumberFields);
            }

            if(cardfield5) cardNumberFields.push(cardfield5);


            if(!monthField) {
              monthField = guessMonthField(formFieldDict.hSelectsDict,true);
            }

            if(!yearField) {
              yearField = guessYearField(formFieldDict.hSelectsDict,true);
            }

            if(!cardTypeField) {
              cardTypeField = guessCardTypeField(formFieldDict.hSelectsDict,true);
            }

            if(!monthField && !yearField) {
               monthField = guessMonthField(formFieldDict.vInputsDict);
               yearField = guessYearField(formFieldDict.vInputsDict);

               if((!monthField && !yearField) || (monthField.pointer === yearField.pointer) ) {
                   expiryField = guessExpiryField(formFieldDict.vInputsDict);
               }

            }

            var formDict = {};
            formDict.htmlName = pform.getAttribute('name');
            formDict.htmlId = pform.getAttribute('id');
            formDict.pointer = pform;
            formDict.htmlClass = (pform.classList) ? Array.prototype.slice.call(pform.classList) : null;
            formDict.cardNumberField = (cardNumberFields.length >= 4) ? null : cardNumberField;
            formDict.cardNumberFields = cardNumberFields;
            formDict.cardHolderField = cardHolderField;
            formDict.cardTypeField = cardTypeField;
            formDict.cvvField = cvvField;
            formDict.monthField = monthField;
            formDict.yearField = yearField;
            formDict.expiryField = expiryField;

            formsDict.push(formDict);
        }

        return formsDict;
    };

    var formScoringFunction =function(formDict) {
        var score = 0;

        if(formDict.cardNumberField || formDict.cardNumberFields.length >= 4) {
            score += 2;
        }else {
            return -LARGE_SCORE;
        }

        score += (formDict.cardHolderField) ? 2 : 0;
        score += (formDict.cardTypeField) ? 2 : 0;
        score += (formDict.cvvField) ? 2 : 0;
        score += (formDict.monthField)? 2 : 0;
        score += (formDict.yearField)? 2 : 0;

        if(score > 0) {
            score += getAttributeScore(formDict.htmlName,FORM_CLASS);
            score += getAttributeScore(formDict.htmlId,FORM_CLASS);
            score += getAttributeScore(formDict.htmlClass,FORM_CLASS);
        }

        return score;
    };

    var guessCreditCardForm = function() {
        var pageForms = getForms();
        var formsDict = createFormDict(pageForms);
        var creditCardForm = getMaximumElement(formsDict,formScoringFunction);

        if(creditCardForm && formScoringFunction(creditCardForm) > 0) {
            return creditCardForm;
        }

        return null;
    };

    var fillCreditCardForm = function(creditCardFormDict,card) {

        var hints = evaluateServerHints();

        if(!creditCardFormDict) { return;}

        if(!hints) {
            if(creditCardFormDict.cardTypeField) {
                fillValue(creditCardFormDict.cardTypeField,card.cardtype,"cardtype");
                creditCardFormDict = guessCreditCardForm();
            }

            if(creditCardFormDict.cardNumberField) {
                fillValue(creditCardFormDict.cardNumberField,card.ccnumber);
                creditCardFormDict = guessCreditCardForm();
            }

            if(creditCardFormDict.cardNumberFields.length >= 4) {
                var totalfields = creditCardFormDict.cardNumberFields.length,
                    ccnumber    = card.ccnumber,
                    start       = 0,
                    end         = 4;

                fillValue(creditCardFormDict.cardNumberFields[0],ccnumber.substring(start,end));

                start = end;
                end   = end + 4;
                fillValue(creditCardFormDict.cardNumberFields[1],ccnumber.substring(start,end));

                start = end;
                end   = end + 4;
                fillValue(creditCardFormDict.cardNumberFields[2],ccnumber.substring(start,end));

                if(totalfields === 4) {
                    start = end;
                    fillValue(creditCardFormDict.cardNumberFields[3],ccnumber.substring(start));
                } else {
                    start = end;
                    end   = end + 4;
                    fillValue(creditCardFormDict.cardNumberFields[3],ccnumber.substring(start,end));

                    start = end;
                    fillValue(creditCardFormDict.cardNumberFields[4],ccnumber.substring(start));
                }
            }

            if(creditCardFormDict.cardHolderField) {
                fillValue(creditCardFormDict.cardHolderField,card.holderName);
            }

            if(creditCardFormDict.monthField) {
              fillValue(creditCardFormDict.monthField,card.month,"month");
            }

            if(creditCardFormDict.yearField) {
              fillValue(creditCardFormDict.yearField,card.year,"year");
            }

            if(creditCardFormDict.expiryField) {
                var month = card.month,
                    year = card.year,
                    length = creditCardFormDict.expiryField.htmlLength,
                    expiryField = creditCardFormDict.expiryField.pointer;

                expiryField.value = month;
                triggerEvent(expiryField,"keyup");

                if(length === 5) {
                    expiryField.value = expiryField.value + year.substr(2);
                } else if(length === 7) {
                    expiryField.value = expiryField.value + year;
                } else {
                    expiryField.value = month+"/"+ year;
                }
            }

            if(creditCardFormDict.cvvField) {
              fillValue(creditCardFormDict.cvvField,card.cvv);
            }

        }
    };

    var exports = {};
    exports.guessCreditCardForm = guessCreditCardForm;
    exports.fillCreditCardForm = fillCreditCardForm;

    return exports;
})();

var creditCardForm = cardModule.guessCreditCardForm();
cardModule.fillCreditCardForm(creditCardForm,clientData);
